-- Copyright (c) 2025 Huawei Technologies Co., Ltd.
-- openUBMC is licensed under Mulan PSL v2.
-- You can use this software according to the terms and conditions of the Mulan PSL v2.
-- You may obtain a copy of Mulan PSL v2 at:
--         http://license.coscl.org.cn/MulanPSL2
-- THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
-- EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
-- MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
-- See the Mulan PSL v2 for more details.

local class = require 'mc.class'
local log = require 'mc.logging'
local consts = require 'constants'
local singleton = require 'mc.singleton'
local property_sync_instance = require 'objects.property_sync'
local data_sync_instance = require 'objects.data_sync'
local sender_instance = require 'tcp.sender'
local receiver_instance = require 'tcp.receiver'
local tcp_mgmt_instance = require 'tcp.tcp_mgmt'
local signal = require 'mc.signal'
local file_sync_instance = require 'objects.file_sync'
local file_sec = require 'utils.file'

local mgmt = class{}

function mgmt:ctor(bus)
    self.bus = bus
    self.property_sync_list = {}
    self.file_sync_list = {}
    self.data_sync_object = nil
    self.tcp_enbaled_sig = signal.new()
    self.address_update_sig = signal.new()
end

local function check_property_sync_valid(obj, name)
    local value = obj.SyncMode
    if value ~= consts.SYNC_MODE_REALTIME and value ~= consts.SYNC_MODE_PERIOD then
        return false, 'SyncMode: ' .. value
    end
    -- 同步服务名/对象名/接口/属性为空时无意义
    if obj.ServiceName == '' then
        return false, 'ServiceName'
    end
    if obj.SyncObjectName == '' then
        return false, 'SyncObjectName'
    end

    local items_invalid = false
    local items = obj.SyncObjectItems
    if not items[1] then
        items_invalid = true
    else
        local interface, props = table.unpack(items[1])
        if not interface or interface == '' or type(interface) ~= 'string' then
            items_invalid = true
        elseif not props or type(props) ~= 'table' or #props == 0 then
            items_invalid = true
        end
    end
    if items_invalid then
        return false, 'SyncObjectItems'
    end

    return true
end

function mgmt:register_property_sync(mdb_obj, communication_type)
    local name = mdb_obj.name
    local ok, err_msg = check_property_sync_valid(mdb_obj)
    if not ok then
        log:error('register [%s] failed, invalid prop: [%s]', name, err_msg)
        return
    end
    local object = property_sync_instance.new(mdb_obj, name)
    self.property_sync_list[name] = object
    object:initialize(self.bus, communication_type, self.tcp_enbaled_sig)
end

local function check_file_sync_valid(obj, name)
    local value = obj.SyncMode
    if value ~= consts.SYNC_MODE_REALTIME and value ~= consts.SYNC_MODE_PERIOD then
        return false, 'SyncMode: ' .. value
    end
    value = obj.SyncType
    if value ~= consts.DATASYNC_FILE_TYPE_AUTO and value ~= consts.DATASYNC_FILE_TYPE_SEND then
        return false, 'SyncType: ' .. value
    end

    -- 校验文件路径是否非法
    for _, path in pairs(obj.SyncFileList) do
        if path == '' or file_sec.check_shell_special_character_s(path) ~= 0 or
            string.match(path, '--checkpoint[=-=]') then
            return false, 'SyncFileList'
        end
    end

    return true
end

function mgmt:register_file_sync(mdb_obj)
    local name = mdb_obj.name
    local ok, err_msg = check_file_sync_valid(mdb_obj)
    if not ok then
        log:error('register [%s] failed, invalid prop: [%s]', name, err_msg)
        return
    end
    local object = file_sync_instance.new(mdb_obj, name)
    self.file_sync_list[name] = object
    object:initialize(self.bus, self.tcp_enbaled_sig)
end

function mgmt:register_datasync(mdb_obj)
    -- 如果配置错误，不开启同步
    local type = mdb_obj.CommunicationType
    if type ~= consts.COMMUNICATION_TYPE_CAN and type ~= consts.COMMUNICATION_TYPE_TCP then
        log:error('init data sync [%s] failed, invalid CommunicationType: [%s]', mdb_obj.name, type)
        return false
    end
    local object = data_sync_instance:get_instance()
    object:register(type)
    self.data_sync_object = object
    return true
end

function mgmt:register_tcp_mgmt(mdb_obj)
    local role = mdb_obj.SyncRole
    -- 如果通信类型配置错误，不开启同步
    if role ~= consts.DATASYNC_ROLE_RECEIVER and role ~= consts.DATASYNC_ROLE_SENDER then
        log:error('register [%s] failed, invalid sync role: [%s]', mdb_obj.name, role)
        return
    end
    tcp_mgmt_instance.get_instance():register(mdb_obj, self.tcp_enbaled_sig, self.address_update_sig, role)
    -- 注册服务端
    receiver_instance.get_instance():register(self.tcp_enbaled_sig, self.address_update_sig)
end

function mgmt:register_sync_destination(mdb_obj)
    sender_instance.get_instance():register(mdb_obj, self.tcp_enbaled_sig)
end

return singleton(mgmt)